#ifndef URSAX_URSAX_H
#define URSAX_URSAX_H

#include <stdlib.h>
//#include <cstdlib>
#include <malloc.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <stdint.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <limits.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "log.h"


#define FILE_NAME_MAX_LEN (255) // if filename len to this, error will happen

#define UNUSED_PARAM(x) do { (void)(x); } while(0)

#define NET_SERVICE_TIMEOUT_LIMIT (60)

#define DEFAULT_VECTOR_SIZE (1024)

#define WORKERS_TRACKER_WAIT_MAX_SECOND (-1)
#define ST_STACK_SIZE (1020*1024)
#define SERVERS_BUF_LEN (16)
#define ONE_GB (1024*1024*1024UL)
#define ONE_MB (1024*1024UL)
#define IO_DEPTH_SHIFT (5)
#define IO_DEPTH (1<<IO_DEPTH_SHIFT)
#define IO_DEPTH_MASK (IO_DEPTH-1)
#define REFERENCE_MAX (200)
#define CLIENT_MAX_FD (10000)
#define CHANGE_WRITE_MODE_LEVEL (4096*2)
#define MEMPOOL_BUFFER_SIZE (1024*1024U)
#define MMAPED_BUFFER_SIZE (MEMPOOL_BUFFER_SIZE + 2 * 4096)

#define SHM_PATH "/dev/shm/"

#define INVALID_VERSION ((unsigned long)(-1L))

#define SIZE_4K (4096)
#define SIZE_1K (1024)

#ifndef likely
#define likely(x)       __builtin_expect(!!(x),1)
#endif

#ifndef unlikely
#define unlikely(x)     __builtin_expect(!!(x),0)
#endif

#ifndef ROUND_UP
#define ROUND_UP(n,d) (((n) + (d) - 1) & -(d))
#endif

#ifndef DIV_ROUND_UP
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#endif

#define LEN(a) (sizeof(a) / sizeof(a[0]))

#ifndef MIN
#define MIN(a, b) (((a)<(b)) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a, b) (((a)>(b)) ? (a) : (b))
#endif

#ifdef __DEBUG
#define DEBUG_FUNC(cmd) do{cmd;}while(0)
#else
#define DEBUG_FUNC(cmd)
#endif
#define DEBUG_INCUBATE DEBUG_FUNC(st_usleep(100*1000))

#ifndef likely
#define likely(x)       __builtin_expect(!!(x),1)
#endif

#ifndef unlikely
#define unlikely(x)     __builtin_expect(!!(x),0)
#endif

#ifndef myoffsetof
#define myoffsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#ifndef container_of
#define container_of(ptr, type, member) ({                      \
			const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr);    \
			(type *)( (char *)__mptr - myoffsetof(type,member) );})
#endif

#define URSAX_DISK_TYPE_HDD (0)
#define URSAX_DISK_TYPE_SSD (1)
#define URSAX_DISK_TYPE_JOURNAL (2)
#define URSAX_DISK_TYPE_SSDJOURNAL (3)
#define URSAX_DISK_TYPE_MAX (4)

#define UPPER_FLOOR(offset, align) (((offset) + align - 1) & (~(align - 1)))
#define LOWER_FLOOR(offset, align) ((offset) & (~(align - 1)))

#ifndef URSAXVERSION
#define URSAXVERSION "no-version"
#endif

#define ALIGIN_MEM(name, aligin_size)  char __buf##name[aligin_size * 2]; \
									   char *name = (char *)(((long)(__buf##name + aligin_size - 1)) & (long)~(aligin_size - 1));

#define begin_proc(...)
#define end_proc(...)

static const uint32_t max_bit_mask = (1<<31);

static inline uint64_t get_uint64(const char *p)
{
    uint64_t value;
    int ret = sscanf(p, "%lu", &value);
    if (unlikely(ret != 1)) {
        LOG_ERROR("error uint64_t: %s", p);
        exit(-1);
    }
    return value;
}

static inline uint64_t parse_size(const char *_size)
{
    char size[64];
    strcpy(size, _size);
    int len = strlen(size);
    uint64_t times = 1;
    uint64_t base;
    if(size[len - 1] == 'B' || size[len - 1] == 'b'){
        size[len - 1] = '\0';
        len --;
    }

    if(isdigit(size[len - 1])) {
        times = 1;
    }
    else {
        static const char *sufs = "KMGTP";
        const char *c = strchr(sufs, toupper(size[len - 1]));
        if(c == NULL) {
            fprintf(stderr, "error size str: %s\n", size);
            exit(-1);
        }
        int pow = c - sufs + 1;
        for(int i = 0; i < pow; i++){
            times *= 1024;
        }
        size[len - 1] = '\0';
        len --;
    }

    base = get_uint64(size);
    return base * times;
}

static inline uint64_t size32_convert_to64(uint32_t size)
{
    if (size & max_bit_mask) {
        return (size ^ max_bit_mask) * 512UL;
    }
    return size;
}

static inline uint32_t size64_convert_to32(uint64_t size)
{
    if (size >= (1UL<<32)) {
        return (size / 512) | max_bit_mask;
    }
    return size;
}

static inline bool is_symbol_link(const char* path)
{
    char buf[PATH_MAX];
    int len = readlink(path, buf, sizeof(buf));
    if(len < 0) {
        if(errno == EINVAL) {
            LOG_INFO("PATH %s, not exist", path);
        }
        return false;
    }
    return true;
}

static inline void get_version_nbs(int *major_version, int *minor_version, int *patch_version)
{
    int ret = sscanf(URSAXVERSION, "%*[^0-9]%d.%d.%d", major_version, minor_version, patch_version);
    if(ret != 3) {
        *major_version = 0;
        *minor_version = 0;
        *patch_version = 0;
    }
}

static inline void* ursax_zalloc(size_t size)
{
    if (unlikely(0 == size))
        return NULL;

    void* buf = calloc(1, size);
    if (buf == NULL) {
        LOG_FATAL("malloc failed when allocating %u bytes", size);
        exit(-1);
    }
    return buf;
}

#define CHAR(n, i) (((unsigned char *)&n)[i])

static inline void* ursax_memalign(size_t boundary, size_t size)
{
    if(unlikely(0 == size))
        return NULL;

    void* buf = memalign(boundary, size);
    if (buf == NULL) {
        LOG_FATAL("memalign failed when allocating %u bytes with boundary %u", size, boundary);
        exit(-1);
    }
    memset(buf, 0, size);
    return buf;
}

static inline uint64_t raw_size(int fd)
{
    uint64_t size;
    int ret = ioctl(fd, BLKGETSIZE64, &size);
    if (unlikely(ret < 0)) {
        LOG_ERROR("unable to fstatfs raw %d: %s", errno, strerror(errno));
        return 0;
    }

    return (unsigned long)size;
}

static inline uint64_t file_size(int fd)
{
    struct stat sb;
    int ret = fstat(fd, &sb);
    if (unlikely(ret < 0)) {
        LOG_ERROR("unable to fstat file %d: %s", errno, strerror(errno));
        return 0;
    }

    return (unsigned long)sb.st_size;
}

static inline void* ursax_4k_align_alloc(size_t size)
{
    if(unlikely(0 == size))
        return NULL;
    return ursax_memalign(SIZE_4K, size);
}

static inline char *str_ip(uint32_t ip)
{
#define MAX_IPS 16
    static char retip[MAX_IPS][32];
    static int p = 0;
    char *retp = retip[p++];
    sprintf(retp, "%u.%u.%u.%u", CHAR(ip, 3),CHAR(ip, 2),CHAR(ip, 1), CHAR(ip, 0));
    if(p == MAX_IPS){
        p = 0;
    }
    return retp;
}

static inline void ursax_free(void *p)
{
    if(unlikely(p == NULL)) {
        return;
    }
    free(p);
}

static inline void set_max_open_fd(int number)
{
    if(number < 1024){
        LOG_ERROR("FD NUMBER %d TOO SMALL, QUIT", number);
        return;
    }

    struct rlimit r;
    int ret;
    r.rlim_max = number;
    r.rlim_cur = r.rlim_max;
    ret = setrlimit(RLIMIT_NOFILE, &r);
    if(0 == ret){
        LOG_INFO("success set open fds to %d", number);
        return;
    }
    LOG_INFO("error set open fd to %d, not have root permission ?", number);

    ret = getrlimit(RLIMIT_NOFILE, &r);
    if(0 != ret){
        // failed
        LOG_ERROR("error getrlimit, failed to set max fd");
        return;
    }
    r.rlim_cur = r.rlim_max; // set max
    ret = setrlimit(RLIMIT_NOFILE, &r);
    if(0 != ret){
        // failed
        LOG_ERROR("error setrlimit, failed to set max fd");
        return;
    }
    LOG_INFO("success set current max fd to %d", r.rlim_cur);
    return;
}

typedef struct Delayer
{
    uint64_t c1;
    uint64_t c2;
    uint64_t incomming_utime_c1;
    uint64_t incomming_utime_c2;
    uint64_t max_incomming_utime;
    uint64_t min_incomming_utime;
    uint64_t doing_utime_c1;
    uint64_t doing_utime_c2;
    uint64_t max_doing_utime;
    uint64_t min_doing_utime;
    int pid;
    struct timeval last_begin_time;
    struct timeval last_end_time;
    const char *str;
} Delayer;

static inline void Delayer_init(Delayer *d, const char* str)
{
    memset(d, 0, sizeof(*d));
    d->str = str;
    d->pid = getpid();
    gettimeofday(&d->last_begin_time, 0);
}

#ifndef zalloc
#define zalloc(x)		ursax_zalloc(x)
#endif

#ifndef free
#define free(x)			ursax_free(x)
#endif

#endif //URSAX_URSAX_H
